﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Spanner;

namespace SpannerWebSite
{
    internal partial class Intro: CommonUI
    {
        internal static SpannerChapter TheChapter;

        static Intro()
        {
            var sections = new List<SpannerSection> { };
            var initActs = new List<Act> { };

            // Write out files we're going to depend on here.  I know, I know, this is
            // very naughty in a static constructor...

            HelloWorld.WriteWebPage();
            HelloWorldLong.WriteWebPage();
            YourNameInLights.WriteWebPage();
            YourNameInFlashingLights.WriteWebPage();

            sections.Add(
                Section("Opening remarks", "OpeningRemarks",
                    P(Spanner, " is designed to help build single-page web applications based on HTML, JavaScript, ",
                    "and the ", Knockout, " MVVM library, without all the usual web development pain. ",
                    "I have tried to avoid getting into fine detail too quickly here, so I ",
                    "encourage the reader to gloss over any code fragments which are not immediately explained.  ",
                    "I hope I have achieved the key design goal that ", Spanner, " be largely self-explanatory. ",
                    "Having said that, please note that this tutorial assumes a working knowledge of HTML, CSS, ",
                    "and JavaScript, and at least some idea of ", Knockout, "'s observable variables.")
                )
            );

            sections.Add(
                Section("The Spanner 'Workflow'", "TheSpannerWorkflow",
                    P("Web development using Spanner proceeds as follows: the developer writes a C# ",
                    "program using the ", Spanner, " library.  When this program is compiled and run, ",
                    "it generates a complete web site - HTML, JavaScript, CSS - which can be served ",
                    "as static web pages.  All interaction is mediated on the client via JavaScript and ",
                    Knockout, "."),

                    Subsection("Why use Spanner?",
                        UL(
                            LI("Because your web site is described as a C# program, you have the advantage ",
                            "of using a single, statically type checked language for ", Em("every"), " aspect ",
                            "of web application development."),
                            LI("Using a single, standard, language means you can use standard abstraction ",
                            "mechanisms for building components of your architecture."),
                            LI("In particular, most errors are flagged by ",
                            "Visual Studio as you type, exactly as with C# development for other kinds of ",
                            "application."),
                            LI(Spanner, " uses HTML, CSS, and JavaScript concepts in a one-to-one fashion, ",
                            "right down to element, function, and type names."),
                            LI("You can have increased confidence in the resulting web application ",
                            "because it, too, has been statically type checked."),
                            LI(Spanner, " integrates painlessly with any third party JavaScript which can be ",
                            "given a well typed API."),
                            LI(Spanner, " imposes minimal policy on your design: you are largely free to ",
                            "arrange your architecture as you see fit."),
                            LI("The generated JavaScript is highly regular and is easy to debug on the client.")
                        )
                    )
                )
            );

            sections.Add(
                Section("Hello, World!", "HelloWorld",
                    P("In traditional style, we begin with ", Em("Hello, World!"),
                    "  Here is the complete C# listing:"),
                    FileFragmentFigure(
                        Group(Em("Hello, World!"), " in ", Spanner, " in C#."),
                        Lang.csharp, "../../Intro.HelloWorld.cs", "", null, ignoreUnindentedLines: false
                    ),
                    P("After compiling and running this program, the directory ",
                    Code(Const.DemoWebSitePath), " will have been created, if it didn't ",
                    "already exist, with subdirectories ", Code("html"), ", ", Code("js"), ", ", Code("css"),
                    ", and ", Code("media"), ".  The ", Code("html"), " subdirectory will contain ",
                    Code("HelloWorld.html"), ", the start page of our web 'application'."),
                    P("Examining the ", Code(".html"), " file, we find this:"),
                    FileFragmentFigure(
                        Group(Em("Hello, World!"), " web page generated by", Spanner, "."),
                        Lang.markup, Path.Combine(Const.DemoWebSitePath, "html", "HelloWorld.html"), "", null,
                        ignoreUnindentedLines: false,
                        filter: StripSpannerRuntimeFilter
                    ),
                    P("The HTML should be immediately familiar and the reader should be able to ",
                    "get the gist of the JavaScript component; ", Spanner, " always generates some ",
                    "JavaScript; this is just what you get for a web 'application' with no logic."),
                    P("The end result, of course, is just:"),
                    Figure(
                        HelloWorld.Content()
                    )
                )
            );

            sections.Add(
                Section("Hello, World! - the long way", "HelloWorldLong",
                    P("Above we used the function ", Code("StandardSpannerHtmlPage"), " to provide ",
                    "the usual HTML boilerplate.  Here we see how we can achieve the same end ",
                    "without taking short-cuts:"),
                    FileFragmentFigure(
                        Group(Em("Hello, World!"), " - the long way."),
                        Lang.csharp, "../../Intro.HelloWorldLong.cs", "", null, ignoreUnindentedLines: false
                    ),
                    P("and here we see that the HTML generated from this ", Spanner, " program is identical ",
                    "to that produced previously (the ", Code("ViewModelJavaScript()"), " expression above is the ",
                    Spanner, " magic for 'include all the relevant JavaScript here'):"),
                    FileFragmentFigure(
                        Group(Em("Hello, World!"), " (the long way) - web page generated by", Spanner, "."),
                        Lang.markup, Path.Combine(Const.DemoWebSitePath, "html", "HelloWorldLong.html"), "", null,
                        ignoreUnindentedLines: false,
                        filter: StripSpannerRuntimeFilter
                    ),
                    P("Once again, the resulting web page is just this:"),
                    Figure(
                        HelloWorldLong.Content()
                    ),
                    P("Now, having done that the long way, in what follows we'll just use ",
                    Code("StandardSpannerHtmlPage"), ".")
                )
            );

            sections.Add(
                Section("HTML in Spanner", "HtmlInSpanner",
                    P("The long version of ", Em("Hello, World!"), " is a good illustration of how ",
                    Spanner, " represents and generates HTML.  Let's look at the source code in more ",
                    "detail."),
                    P("First, observe that the class containing our code inherits from ", Code("UI"),
                    " the root ", Spanner, " class: ", Code("class Intro: UI"), ".  This is a useful idiom ",
                    "which means we don't have to explicitly prefix every ", Spanner, " function use ",
                    "with ", Code("UI"), "(e.g., ", Code("UI.P(...)"), ", ", Code("UI.Html(...)"),
                    " etc.)."),
                    P("Second, observe how the HTML for our web page is described in ", Spanner, ":"),
                    FileFragmentFigure(null,
                        Lang.csharp, "../../Intro.HelloWorldLong.cs",
                        "Html Content", "// END"
                    ),
                    P("and compare that with the generated HTML.  We immediately see some ",
                    "obvious correspondences:"),
                    UL(
                        LI(Code("Doctype(...)"), " corresponds to ", Code("<!DOCTYPE html>...")),
                        LI(Code("Html(...)"), " corresponds to ", Code("<html>...</html>")),
                        LI(Code("Head(...)"), " corresponds to ", Code("<head>...</head>")),
                        LI(Code("Title(...)"), " corresponds to ", Code("<title>...</title>")),
                        LI(Code("Body(...)"), " corresponds to ", Code("<body>...</body>")),
                        LI(Code("P(...)"), " corresponds to ", Code("<p>...</p>"))
                    ),
                    P("Indeed, ", Em("every"), " HTML5 element is represented directly in ", Spanner,
                    " in this fashion: ",
                    Code("A"), "; ",
                    Code("Abbr"), "; ",
                    Code("Address"), "; ",
                    Code("Area"), "; ",
                    Code("Article"), "; ",
                    Code("Aside"), "; ",
                    Code("Audio"), "; ",
                    Code("B"), "; ",
                    Code("Base"), "; ",
                    Code("Bdi"), "; ",
                    Code("Bdo"), "; ",
                    Code("BlockQuote"), "; ",
                    Code("Body"), "; ",
                    Code("BR"), "; ",
                    Code("Button"), "; ",
                    Code("Canvas"), "; ",
                    Code("Caption"), "; ",
                    Code("CDATA"), "; ",
                    Code("Cite"), "; ",
                    Code("Code"), "; ",
                    Code("Col"), "; ",
                    Code("ColGroup"), "; ",
                    Code("Command"), "; ",
                    Code("Data"), "; ",
                    Code("DataGrid"), "; ",
                    Code("DataList"), "; ",
                    Code("DD"), "; ",
                    Code("Del"), "; ",
                    Code("Details"), "; ",
                    Code("Doctype"), "; ",
                    Code("Dfn"), "; ",
                    Code("Div"), "; ",
                    Code("DL"), "; ",
                    Code("DT"), "; ",
                    Code("Em"), "; ",
                    Code("Embed"), "; ",
                    Code("EventSource"), "; ",
                    Code("FieldSet"), "; ",
                    Code("FigCaption"), "; ",
                    Code("Figure"), "; ",
                    Code("Footer"), "; ",
                    Code("Form"), "; ",
                    Code("H1"), "; ",
                    Code("H2"), "; ",
                    Code("H3"), "; ",
                    Code("H4"), "; ",
                    Code("H5"), "; ",
                    Code("H6"), "; ",
                    Code("Head"), "; ",
                    Code("Header"), "; ",
                    Code("HGroup"), "; ",
                    Code("HR"), "; ",
                    Code("Html"), "; ",
                    Code("I"), "; ",
                    Code("IFrame"), "; ",
                    Code("Img"), "; ",
                    Code("Input"), "; ",
                    Code("Ins"), "; ",
                    Code("Kbd"), "; ",
                    Code("KeyGen"), "; ",
                    Code("Label"), "; ",
                    Code("Legend"), "; ",
                    Code("LI"), "; ",
                    Code("Link"), "; ",
                    Code("Mark"), "; ",
                    Code("ImageMap"), "; ",
                    Code("Menu"), "; ",
                    Code("Meta"), "; ",
                    Code("Meter"), "; ",
                    Code("Nav"), "; ",
                    Code("Noscript"), "; ",
                    Code("Object"), "; ",
                    Code("OL"), "; ",
                    Code("OptGroup"), "; ",
                    Code("Option"), "; ",
                    Code("Output"), "; ",
                    Code("P"), "; ",
                    Code("Param"), "; ",
                    Code("Pre"), "; ",
                    Code("Progress"), "; ",
                    Code("Q"), "; ",
                    Code("Ruby"), "; ",
                    Code("RP"), "; ",
                    Code("RT"), "; ",
                    Code("S"), "; ",
                    Code("Samp"), "; ",
                    Code("Script"), "; ",
                    Code("Section"), "; ",
                    Code("Select"), "; ",
                    Code("Small"), "; ",
                    Code("Source"), "; ",
                    Code("Span"), "; ",
                    Code("Strong"), "; ",
                    Code("Style"), "; ",
                    Code("Sub"), "; ",
                    Code("Summary"), "; ",
                    Code("Sup"), "; ",
                    Code("Table"), "; ",
                    Code("Tbody"), "; ",
                    Code("TD"), "; ",
                    Code("TextArea"), "; ",
                    Code("TFoot"), "; ",
                    Code("TH"), "; ",
                    Code("THead"), "; ",
                    Code("Time"), "; ",
                    Code("Title"), "; ",
                    Code("TR"), "; ",
                    Code("Track"), "; ",
                    Code("U"), "; ",
                    Code("UL"), "; ",
                    Code("VAR"), "; ",
                    Code("Video"), "; and ",
                    Code("Wbr"), "."),
                    P("Each of these functions returns an immutable value of type ", Code("Html"), ". ",
                    "Each can also take any number of ", Code("Html"), " values as children, with the ",
                    "following exceptions: ",
                    Code("Area"), ", ",
                    Code("Base"), ", ",
                    Code("BR"), ", ",
                    Code("Col"), ", ",
                    Code("Command"), ", ",
                    Code("Embed"), ", ",
                    Code("HR"), ", ",
                    Code("Img"), ", ",
                    Code("KeyGen"), ", ",
                    Code("Meta"), ", ",
                    Code("Param"), ", ",
                    Code("Source"), ", ",
                    Code("Track"), ", and ",
                    Code("Wbr"), ", which accept no arguments; ",
                    Code("CDATA"), ", which accepts any number of verbatim strings; and ",
                    Code("Comment"), ", which accepts a single string and generates an HTML ",
                    Code("<!--"), " comment ", Code("-->"), ". ",
                    "Note that ordinary strings are implicitly cast to ", Code("Html"),
                    " where required.")
                )
            );

            var yourNameInFlashingLights = YourNameInFlashingLights.Content();
            initActs.Add(yourNameInFlashingLights.InitAct);
            sections.Add(
                Section("Knockout, JavaScript, and Spanner", "KoJSAndSpanner",
                    Subsection("Your Name in Lights",
                        P("Next, let's look at a simple interactive web application written using ", Spanner, ".  ",
                        "In this example, the user is asked to enter their name, whereupon they will see it ",
                        "writ large, in capitals, surrounded by 'lights':"),
                        Figure(
                            YourNameInLights.Content()
                        ),
                        P("Here is the significant code for ", Code("YourNameInLights.cs"), ":"),
                        FileFragmentFigure(
                            Group(Em("Your Name in Lights"), " in ", Spanner, "."),
                            Lang.csharp, "../../Intro.YourNameInLights.cs",
                            "internal static Html Content",
                            "internal static void Write",
                            ignoreUnindentedLines: false
                        ),
                        P("Examining this piece by piece:"),
                        UL(
                            LI(Code("var name = ObsVar<string>(\"name\", \"\")"), " defines a new ",
                            "string valued ", Em("observable variable"), " (a ", Knockout, " observable) ",
                            "initialised to the empty string.  The first argument to ", Code("ObsVar"), " ",
                            "is optional and specifies the name this variable should have in the generated ",
                            "JavaScript.  (", Spanner, " will add unique suffixes to names where required ",
                            "to ensure uniqueness.)  Observables are the core idea of the MVVM pattern, ",
                            "where any change to an observable automatically triggers updates on any ",
                            "dependent computed observables and HTML properties - no bookkeeping is required ",
                            "on the part of the developer."),
                            LI(Code("Group(E1, E2, E3, ...)"), " is a useful pseudo-element representing the ",
                            "'unenclosed' sequence of elements ",
                            Code("E1"), ", ", Code("E2"), ", ", Code("E3"), ", ... ",
                            "In the generated HTML, these elements will appear in that sequence, with ",
                            "no enclosing parent element.  (We could use a plain ", Code("Div"), ", but ",
                            "that is redundant since these elements will already have a parent in the ",
                            Code("Body"), " element.)"),
                            LI(Code("P(\"Please tell me your name: \", ...)"), " denotes an HTML ",
                            Code("<p>...</p>"), " element with two children, the first being a plain text ",
                            "string. (", Spanner, " knows which elements have closing tags and which don't; you ",
                            "have no need to remember which.)"),
                            LI(Code("Input().KoName(name)"), " denotes the following HTML: ",
                            Code("<input data-bind='value: name'>"), ".  The ", Knockout, " ", Code("data-bind"),
                            " attribute applied in this case via the call ", Code("KoName(name)"), "."),
                            LI(Code("Div().KoVisible(name != \"\").WithChildren(...)"), " denotes the ",
                            "following HTML: ", Code("<div data-bind='visible: name() != \"\"'>...</div>"), ". ",
                            "Observe that ", Spanner, " knows that the observable ", Em("name"), " should not ",
                            "be dereferenced when used in the ", Code("value"), " binding above, but should be ",
                            "dereferenced - ", Code("name()"), " - when used in an expression, as in the ",
                            Code("visible"), " binding, here. ",
                            "The idiom ", Code("Div().[attrs].WithChildren(...)"), " is equivalent to writing ",
                            Code("Div(...).[attrs]"), ", but it often reads better when attributes are specified ",
                            "prior to the children of an element."),
                            LI(Code("Show(ToUpperCase(name)).WithAttr(\"style\", ...).WithAttr(\"style\", ...).WithAttr(\"style\", ...)"),
                            " denotes the HTML ", Code("<span data-bind='text: name().toUpperCase()' style='{..., ..., ...}'></span>"),
                            ".  The function ", Code("Show"), " produces a ", Code("<span>"), " element with a ",
                            Knockout, " ", Code("text"), " binding showing the value of its string ",
                            "or number valued argument.  (Building libraries of utility combinators ",
                            "like ", Code("Show"), " in ", Spanner, " is trivial and natural.)  The other ",
                            "point illustrated here is that ",
                            Spanner, " understands that ", Code("style"), " attributes should be grouped together ",
                            "in { braces }.")
                        ),
                        P("(Observe that ", Spanner, " uses an applicative(functional) style, ",
                        Code("ToUpperCase(name)"), ", rather than an object.method style, ", Code("name.toUpperCase()"),
                        " - this is a consequence following from implementing a domain specific language in C#.)"),
                        P("Here is the HTML generated by the above Spanner program:"),
                        FileFragmentFigure(
                            Group(Em("Your Name in Lights"), " web page generated by", Spanner, "."),
                            Lang.markup, Path.Combine(Const.DemoWebSitePath, "html", "YourNameInLights.html"), "", null,
                            ignoreUnindentedLines: false,
                            filter: StripSpannerRuntimeFilter
                        ),
                        P("Some final points of explanation, here:"),
                        UL(
                            LI(Spanner, " automatically imports ", Knockout, ".  The particular version and URL ",
                            "can be overridden by the programmer."),
                            LI("The ", Spanner, " runtime is always included.  This is a handful of ",
                            "straightforward definitions on which the translation into JavaScript depends."),
                            LI("The ", Em("view model"), ", ", Code("YourNameInLights"), ", is defined via a ",
                            "function encapsulating all ",
                            "variables and functions reachable from the HTML and the initialisation action ",
                            "(which is absent in this case). ", Spanner, " automatically orders variable ",
                            "definitions to satisfy dependencies."),
                            LI(Code("spanner.windowOnLoad"), ", from the ", Spanner, " runtime, ",
                            "appends a function to a list of functions to be ",
                            "run on the ", Code("onload"), " event, namely when all the resources on which the ",
                            "page depends have been loaded by the client.  In this case the function calls ",
                            Knockout, "'s ", Code("ko.applyBindings"), " method, which is how the application is ",
                            "started.")
                        )
                    ),
                    Subsection("Your Name in Flashing Lights",
                        P("Now we'll improve the ", Em("Your Name in Lights"), " application with a little ",
                        "animation:"),
                        Figure(
                            yourNameInFlashingLights.BodyHtml
                        ),
                        P("Here is the significant code for ", Code("YourNameInFlashingLights.cs"), ":"),
                        FileFragmentFigure(
                            Group(Em("Your Name in Flashing Lights"), " in ", Spanner, "."),
                            Lang.csharp, "../../Intro.YourNameInFlashingLights.cs",
                            "",
                            null,
                            ignoreUnindentedLines: false
                        ),
                        P("Let's examine the new features:"),
                        UL(
                            LI(Code("class BodyHtmlAndInitAct"), " is just a convenience type pairing ",
                            "some HTML with a corresponding ", Em("initialisation act"), ".  ", Spanner, " ",
                            "uses the term ", Em("act"), " (or ", Em("action"), ") to denote operations carried ",
                            "out for their side-effects (as opposed to ", Em("functions"), " ",
                            "which are used to compute values)."),
                            LI(Code("var counter = ObsVar<int>(\"counter\", 0)"), " defines an integer ",
                            Code("counter"), " observable variable used to control the flashing lights effect."),
                            LI(Code("var initAct = Do(SetInterval(Proc(Set(counter, counter + 1)), 750))"), " ",
                            "defines the ", Em("initialisation act"), " for this view model.  In this case ",
                            "the action is equivalent to the JavaScript ",
                            Code("setInterval(function () { counter = counter + 1; }, 750)"), ".  The following ",
                            "points expand on this line of code."),
                            LI(Code("Set(counter, counter + 1)"), " is obviously the action of incrementing the ",
                            "variable ", Code("counter"), ".  ", Code("Set"), " is used for all assignment ",
                            "in ", Spanner, ".  Observe that no special syntax is required merely because ",
                            Code("counter"), " is a ", Knockout, " observable (in ", Knockout, " this must be ",
                            "written ", Code("counter(counter() + 1)"), "; ", Spanner, " syntax is marginally ",
                            "more intuitive in this regard)."),
                            LI(Code("Proc(Set(counter, counter + 1))"), " defines an inline ", Em("procedure"), ", ",
                            "that is a function whose only purpose is its side effect, equivalent to the JavaScript ",
                            Code("function () { counter(counter() + 1); }"), " in this case."),
                            LI(Code("Do(SetInterval(..., ...))"), " is equivalent to the JavaScript ",
                            Code("setInterval(..., ...)"), ".  The ", Code("Do"), " act is used to evaluate ",
                            "a function purely for its side-effect - in this case ", Code("SetInterval"), " ",
                            "returns an ID, which we don't need here, which can be used to cancel the deferred ",
                            "computation.  Now, returning to the main body of the code..."),
                            LI(Code("KoCss(CssClassCondition(\"yellow-lights\", counter % 4 == 0), ...)"), " ",
                            "denotes the ", Knockout, " ", Code("css"), " binding taking a list of static CSS ",
                            "classes, each accompanied by a condition dictating when the class should be ",
                            "applied to the HTML element."),
                            LI("Finally, because this example requires some initialisation, we must pass ",
                            Code("initAct"), " (the initialisation action) to the ", Code("WebPage"), " function. ",
                            "This ensures the initialisation action will be carried out after all the variables ",
                            "have been created, but before the ", Knockout, " ", Code("applyBindings"), " call ",
                            "is made.")
                        ),
                        P("And here is the HTML generated by the Spanner program:"),
                        FileFragmentFigure(
                            Group(Em("Your Name in Flashing Lights"), " web page generated by", Spanner, "."),
                            Lang.markup, Path.Combine(Const.DemoWebSitePath, "html", "YourNameInFlashingLights.html"), "", null,
                            ignoreUnindentedLines: false,
                            filter: StripSpannerRuntimeFilter
                        )
                    ),
                    Subsection("And there's more...",
                        P("The above provides the flavour of web application development using ", Spanner, ". ",
                        "The reference section explains things in detail, including how to use arrays, computed ",
                        "observables, ordinary JavaScript variables and functions, templates, libraries, ",
                        "interfacing with POCOs, interfacing with third-party JavaScript code, and so forth.")
                    )
                )
            );

            TheChapter = CommonUI.Chapter("Introduction and Tutorial", "Intro", sections, initActs);
        }
    }
}
